From: David Härdeman Date: Sat, 20 Sep 2025 17:48:13 +0000 (+0200) Subject: odhcpd: support assignments on the basis of IAID X-Git-Url: http://git.openwrt.org/%22https:/collectd.org//%22http:/www.crowdsec.net/%22/%22https:/collectd.org/%22http:/www.crowdsec.net/%22?a=commitdiff_plain;h=aebc647a6b7bff705a65087871a45b15effe4790;p=project%2Fodhcpd.git odhcpd: support assignments on the basis of IAID With this patch, IAIDs are actually taken into account when creating assignments, which allows per-IAID assignments. The old odhcpd behaviour is still kept for assignments which only specify a DUID. That behaviour is a first-come-first-serve basis, where the first request (no matter what the IAID is) will get the static address, and later requests with different IAIDs will get a NoAddrsAvail error message (in other words, assignments are not re-assigned and random adresses are not provided when the "main" address is already assigned, if the DUID is known in a static config). The old odhcpd behaviour can be described as follows: Imagine a "dhcp" configuration file with a single static assignment: config host option name 'test' option ip '192.168.44.2' option mac '00:00:00:00:ca:fe' option duid '0003000100000000cafe' option hostid '02' And odhcpd running on an interface with IPv6 addr fd00:aaaa::1/48, using a simple test client (command line parameters should be self-evident here, foo-client is the client part of a veth interface pair, odhcpd is listening to the peer interface foo-server): $ sudo dhcpdig --iaid=0x01 --duid=f000 foo-client IPv6: fd00:aaaa::9c5 $ sudo dhcpdig --iaid=0x01 --duid=f000 foo-client IPv6: fd00:aaaa::9c5 $ sudo dhcpdig --iaid=0x02 --duid=f000 foo-client IPv6: fd00:aaaa::817 $ sudo dhcpdig --iaid=0x02 --duid=f000 foo-client IPv6: fd00:aaaa::817 $ sudo dhcpdig --iaid=0x01 --duid=f000 foo-client IPv6: fd00:aaaa::9c5 $ sudo dhcpdig --iaid=0x01 --duid=0003000100000000cafe foo-client IPv6: fd00:aaaa::2 $ sudo dhcpdig --iaid=0x01 --duid=0003000100000000cafe foo-client IPv6: fd00:aaaa::2 $ sudo dhcpdig --iaid=0x02 --duid=0003000100000000cafe foo-client STATUS: NoAddrsAvail (2) IOW, unknown DUIDs get randomly assigned per-IAID IPv6 addresses. Known statically determined DUIDs will be awarded to the first client attempting a lease, no matter what the IAID is, but the IAID will be remembered and subsequent requests with other IAIDs will fail. With this patch applied, assume a "dhcp" configuration file with the following assignments: config host option name 'testcafe' option ip '192.168.44.2' option mac '00:00:00:00:ca:fe' option hostid '02' option duid '0003000100000000cafe%123' config host option name 'testbeef' option ip '192.168.44.3' option mac '00:00:00:00:be:ef' option hostid '03' option duid '0003000100000000beef' config host option name 'testfood' option ip '192.168.44.4' option mac '00:00:00:00:f0:0d' option hostid '04' option duid '0003000100000000f00d' config host option name 'testfood2' option ip '192.168.44.5' option mac '00:00:00:00:f0:0d' option hostid '05' option duid '0003000100000000f00d%123' Now, using the same test client: $ sudo ./build/dhcpdig --iaid=0x000 --duid=0003000100000000cafe foo-client IPv6: fd00:aaaa::9c4 $ sudo ./build/dhcpdig --iaid=0x123 --duid=0003000100000000cafe foo-client IPv6: fd00:aaaa::2 $ sudo ./build/dhcpdig --iaid=0x000 --duid=0003000100000000cafe foo-client IPv6: fd00:aaaa::9c4 $ sudo ./build/dhcpdig --iaid=0xabc --duid=0003000100000000beef foo-client IPv6: fd00:aaaa::3 $ sudo ./build/dhcpdig --iaid=0x123 --duid=0003000100000000beef foo-client STATUS: NoAddrsAvail (2) $ sudo ./build/dhcpdig --iaid=0xabc --duid=0003000100000000f00d foo-client IPv6: fd00:aaaa::4 $ sudo ./build/dhcpdig --iaid=0xabc --duid=0003000100000000f00d foo-client IPv6: fd00:aaaa::4 $ sudo ./build/dhcpdig --iaid=0x123 --duid=0003000100000000f00d foo-client IPv6: fd00:aaaa::5 See also: https://github.com/openwrt/odhcpd/issues/175 Signed-off-by: David Härdeman Link: https://github.com/openwrt/odhcpd/pull/255 Signed-off-by: Álvaro Fernández Rojas --- diff --git a/src/config.c b/src/config.c index 5f072ea..445b97a 100644 --- a/src/config.c +++ b/src/config.c @@ -1671,16 +1671,30 @@ static void lease_update(_unused struct vlist_tree *tree, struct vlist_node *nod lease_delete(lease_old); } -struct lease *config_find_lease_by_duid(const uint8_t *duid, const uint16_t len) +/* + * Either find: + * a) a lease with an exact DUID/IAID match; or + * b) a lease with a matching DUID and no IAID set + */ +struct lease *config_find_lease_by_duid_and_iaid(const uint8_t *duid, const uint16_t len, + const uint32_t iaid) { - struct lease *l; + struct lease *l, *candidate = NULL; vlist_for_each_element(&leases, l, node) { - if (l->duid_len == len && !memcmp(l->duid, duid, len)) + if (l->duid_len != len || memcmp(l->duid, duid, len)) + continue; + + if (!l->iaid_set) { + candidate = l; + continue; + } + + if (l->iaid == iaid) return l; } - return NULL; + return candidate; } struct lease *config_find_lease_by_mac(const uint8_t *mac) diff --git a/src/dhcpv6-ia.c b/src/dhcpv6-ia.c index 549256f..f24f14c 100644 --- a/src/dhcpv6-ia.c +++ b/src/dhcpv6-ia.c @@ -488,7 +488,7 @@ void dhcpv6_ia_write_statefile(void) if (!(ctxt.c->flags & OAF_BOUND) || ctxt.c->managed_size < 0) continue; - char duidbuf[264]; + char duidbuf[DUID_HEXSTRLEN]; odhcpd_hexlify(duidbuf, ctxt.c->clid_data, ctxt.c->clid_len); @@ -1389,7 +1389,6 @@ static bool dhcpv6_ia_on_link(const struct dhcpv6_ia_hdr *ia, struct dhcp_assign ssize_t dhcpv6_ia_handle_IAs(uint8_t *buf, size_t buflen, struct interface *iface, const struct sockaddr_in6 *addr, const void *data, const uint8_t *end) { - struct lease *l; struct dhcp_assignment *first = NULL; const struct dhcpv6_client_header *hdr = data; time_t now = odhcpd_time(); @@ -1398,7 +1397,7 @@ ssize_t dhcpv6_ia_handle_IAs(uint8_t *buf, size_t buflen, struct interface *ifac uint8_t *clid_data = NULL, mac[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; size_t hostname_len = 0, response_len = 0; bool notonlink = false, rapid_commit = false, accept_reconf = false; - char duidbuf[261], hostname[256]; + char duidbuf[DUID_HEXSTRLEN], hostname[256]; dhcpv6_for_each_option(start, end, otype, olen, odata) { if (otype == DHCPV6_OPT_CLIENTID) { @@ -1410,7 +1409,7 @@ ssize_t dhcpv6_ia_handle_IAs(uint8_t *buf, size_t buflen, struct interface *ifac else if (olen == 10 && odata[0] == 0 && odata[1] == 3) memcpy(mac, &odata[4], sizeof(mac)); - if (olen <= 130) + if (olen <= DUID_MAX_LEN) odhcpd_hexlify(duidbuf, odata, olen); } else if (otype == DHCPV6_OPT_FQDN && olen >= 2 && olen <= 255) { uint8_t fqdn_buf[256]; @@ -1425,13 +1424,9 @@ ssize_t dhcpv6_ia_handle_IAs(uint8_t *buf, size_t buflen, struct interface *ifac rapid_commit = true; } - if (!clid_data || !clid_len || clid_len > 130) + if (!clid_data || !clid_len || clid_len > DUID_MAX_LEN) goto out; - l = config_find_lease_by_duid(clid_data, clid_len); - if (!l) - l = config_find_lease_by_mac(mac); - dhcpv6_for_each_option(start, end, otype, olen, odata) { bool is_pd = (otype == DHCPV6_OPT_IA_PD); bool is_na = (otype == DHCPV6_OPT_IA_NA); @@ -1443,6 +1438,11 @@ ssize_t dhcpv6_ia_handle_IAs(uint8_t *buf, size_t buflen, struct interface *ifac size_t ia_response_len = 0; uint8_t reqlen = (is_pd) ? 62 : 128; uint32_t reqhint = 0; + struct lease *l; + + l = config_find_lease_by_duid_and_iaid(clid_data, clid_len, ntohl(ia->iaid)); + if (!l) + l = config_find_lease_by_mac(mac); /* Parse request hint for IA-PD */ if (is_pd) { diff --git a/src/odhcpd.h b/src/odhcpd.h index e225108..e8ce5a9 100644 --- a/src/odhcpd.h +++ b/src/odhcpd.h @@ -182,6 +182,7 @@ struct config { /* 2-byte type + 128-byte DUID, RFC8415, §11.1 */ #define DUID_MAX_LEN 130 +#define DUID_HEXSTRLEN (DUID_MAX_LEN * 2 + 1) struct lease { struct vlist_node node; @@ -510,7 +511,8 @@ bool odhcpd_bitlen2netmask(bool v6, unsigned int bits, void *mask); bool odhcpd_valid_hostname(const char *name); int config_parse_interface(void *data, size_t len, const char *iname, bool overwrite); -struct lease *config_find_lease_by_duid(const uint8_t *duid, const uint16_t len); +struct lease *config_find_lease_by_duid_and_iaid(const uint8_t *duid, const uint16_t len, + const uint32_t iaid); struct lease *config_find_lease_by_mac(const uint8_t *mac); struct lease *config_find_lease_by_hostid(const uint64_t hostid); struct lease *config_find_lease_by_ipaddr(const uint32_t ipaddr);